﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Common.Utilities;

namespace Workflows.Components.Entities.Distances.FieldDistances
{
    public class OrgNameComparer:IAttributeDistanceCalcutor
    {
        private Dictionary<string, string> _SynonymDictionary;
        /// <summary>full name -> synonyms</summary>
        public Dictionary<string, string> SynonymDictionary
        {
            get
            {
                return this._SynonymDictionary;
            }
            set
            {
                this._SynonymDictionary = value;
            }
        }

        private List<string> _StopWords;
        /// <summary></summary>
        public List<string> StopWords
        {
            get
            {
                return this._StopWords;
            }
            set
            {
                this._StopWords = value;
            }
        }

        #region Implementation of IAttributeComparer

        /// <summary>
        /// load dictionary into memory or read config settings from file
        /// </summary>
        public void InitInMemorySettings()
        {
            this._SynonymDictionary = new Dictionary<string, string>();
            // university 
            {
                string fullName1 = "university";
                List<string> synonyms1 = new List<string>();
                synonyms1.Add("university");
                synonyms1.Add("univ");
                synonyms1.Add("univ.");
                synonyms1.Add("u.");
                foreach (string synonym in synonyms1)
                    this._SynonymDictionary.Add(synonym, fullName1);
            }
            // institute
            {
                string fullName2 = "institute";
                List<string> synonyms2 = new List<string>();
                synonyms2.Add("institute");
                synonyms2.Add("inst");
                synonyms2.Add("inst.");
                synonyms2.Add("institution");
                foreach (string synonym in synonyms2)
                    this._SynonymDictionary.Add(synonym, fullName2);
            }
            // company 
            {
                string fullName3 = "inc";
                List<string> synonyms3 = new List<string>();
                synonyms3.Add("inc");
                synonyms3.Add("inc.");
                synonyms3.Add("incorporated.");
                synonyms3.Add("llc");
                synonyms3.Add("llc.");
                synonyms3.Add("co.");
                synonyms3.Add("co");
                foreach (string synonym in synonyms3)
                    this._SynonymDictionary.Add(synonym, fullName3);
            }
            // and 
            {
                string fullName = "and";
                List<string> synonyms = new List<string>();
                synonyms.Add("and");
                synonyms.Add("&");
                foreach (string synonym in synonyms)
                    this._SynonymDictionary.Add(synonym, fullName);
            }
            this._StopWords = new List<string>();
            this._StopWords.Add("the");
            this._StopWords.Add(",");
            this._StopWords.Add("of");
            this._StopWords.Add("-");
        }

        /// <summary>
        /// return similarity score in [0,1]
        /// 0 indicate no similarity or null match
        /// 1 indicate perfect match
        /// </summary>
        /// <param name="attrValue1"></param>
        /// <param name="attrValue2"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public double Compare(object attrValue1, object attrValue2, ref MatchResult result)
        {
            result = MatchResult.NullMatch;
            if (attrValue1 == null || attrValue2 == null ||
                attrValue1.ToString().Length == 0 || attrValue2.ToString().Length == 0)
                return 0.0;
            string name1 = attrValue1.ToString().ToLower().Trim();
            foreach (string stopWord in this._StopWords)
            {
                name1 = name1.Replace(stopWord, " ");
            }
            string[] words1 = StringUtil.Split(name1, " ");
            List<string> wordList1 = new List<string>();
            foreach (string word in words1)
            {
                if (!string.IsNullOrEmpty(word) &&
                    !this._StopWords.Contains(word))
                {
                    if (this._SynonymDictionary.ContainsKey(word))
                        wordList1.Add(this._SynonymDictionary[word]);
                    else
                        wordList1.Add(word);
                }
            }
            words1 = wordList1.ToArray();
            string name2 = attrValue2.ToString().ToLower().Trim();
            foreach (string stopWord in this._StopWords)
            {
                name2 = name2.Replace(stopWord, " ");
            }
            string[] words2 = StringUtil.Split(name2, " ");
            List<string> wordList2 = new List<string>();
            foreach (string word in words2)
            {
                if (!string.IsNullOrEmpty(word) &&
                    !this._StopWords.Contains(word))
                {
                    if (this._SynonymDictionary.ContainsKey(word))
                        wordList2.Add(this._SynonymDictionary[word]);
                    else
                        wordList2.Add(word);
                }
            }
            words2 = wordList2.ToArray();
            string[] commonWords = StringUtil.Intersection(words1, words2);
            double compareScore = 0.0;
            if (commonWords != null && commonWords.Length > 0)
            {
                string[] allWords = StringUtil.Unions(words1, words2);
                compareScore = ((double)commonWords.Length) / allWords.Length;
            }
            if (compareScore == 0)
            {
                result = MatchResult.NegativeMatch;
            }
            else if(compareScore>0)
                result = MatchResult.PositiveMatch;
            return compareScore;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="entity1"></param>
        /// <param name="entity2"></param>
        /// <param name="attrMetaData"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public double Compare(Entity entity1, Entity entity2, AttributeMetaData attrMetaData, ref MatchResult result)
        {
            Debug.Assert(!string.IsNullOrEmpty(attrMetaData.AttributeName));

            string attrName = attrMetaData.AttributeName;
            object attrValue1 = null;
            object attrValue2 = null;
            if (entity1.ComparableAttributes.ContainsKey(attrName))
                attrValue1 = entity1.ComparableAttributes[attrName];
            if (entity2.ComparableAttributes.ContainsKey(attrName))
                attrValue2 = entity2.ComparableAttributes[attrName];
            return this.Compare(attrValue1, attrValue2, ref result);
        }

        /// <summary>
        /// argument to bind attribute name
        /// </summary>
        public string[] ArgumentNames
        {
            get
            {
                return new string[] { "OrgName" };
            }
        }

        public Dictionary<string, string> ComparerSettings
        {
            get { return new Dictionary<string, string>(); }
            set { }
        }

        #endregion
    }
}
